﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace HexChessBoard
{
    public static class BoardSupport
    {
        /// <summary>
        /// Assigns available moves to every piece on the board for the particular color.
        /// </summary>
        /// <param name="board"></param>
        /// <param name="currentTurn"></param>
        /// <param name="ConsiderIfKingInCheck"></param>
        public static void GetAllSideAvailableMoves(Board board, PieceColor currentTurn, bool ConsiderIfKingInCheck)
        {
            if (ConsiderIfKingInCheck)
                board.OpposingMoves.Clear();

            for (int i = 0; i < board.Tiles.Length; i++)
            {
                if (board.Tiles[i].Piece != PieceType.None && board.Tiles[i].Side == currentTurn)
                {
                    board.Tiles[i].AvailableMoves.Clear();

                    // NOTE: knight/bishop can jump both sides.
                    switch (board.Tiles[i].Piece)
                    {
                        case PieceType.Bishop:
                            BoardSupport.GetBishopMoves(board.Tiles[i], ref board.Tiles[i].AvailableMoves);
                            break;
                        case PieceType.King:
                            BoardSupport.GetKingMoves(board.Tiles[i], ref board.Tiles[i].AvailableMoves);
                            break;
                        case PieceType.Knight:
                            BoardSupport.GetKnightMoves(board.Tiles[i], ref board.Tiles[i].AvailableMoves);
                            break;
                        case PieceType.Pawn:
                            BoardSupport.GetPawnMoves(board.Tiles[i], ref board.Tiles[i].AvailableMoves, board.Variant);
                            break;
                        case PieceType.Queen:
                            BoardSupport.GetQueenMoves(board.Tiles[i], ref board.Tiles[i].AvailableMoves);
                            break;
                        case PieceType.Rook:
                            BoardSupport.GetRookMoves(board.Tiles[i], ref board.Tiles[i].AvailableMoves);
                            break;
                    }

                    if (ConsiderIfKingInCheck)
                    {
                        // 1: King cannot move into check, so remove all king tiles that would place into check.
                        //      Do this by creating a board copy for each king move and capture, and checking if the king is then in check.
                        // 2: King cannot capture a piece that leaves him in check, so remove all attackable tiles that place into check
                        //      Do this by taking each piece by duplicating the board, taking each capturable by a king, removing it and moving the king
                        //      and then searching if the king is in check from the new position.
                        // 3: No piece can move if it leaves the king in check, so remove all piece movements that leave the king in check
                        //      Do this by taking the duplicate board, and checking each available move and making sure the king will not be in check after.
                        // 4: No piece can capture if it leaves the king in check, so remove all piece movements that leave the king in check
                        //      Do this by taking the duplicate board, and checking each available capture and making sure the king will not be in check after.
                        // 5: If the king is in check, a king can only move where it is out of check.
                        //      Covered by #1.
                        // 6: If the king is in check, a friendly piece can capture the threatening piece, as long as it does not open other check
                        //      Covered by numbers #2/#4
                        // 7: If the king is in check, a piece can block the lateral movement of pawns, queens, rooks, and bishops.
                        //      Covered by #3
                        Board compareBoard;
                        Stack<int> indexesToRemove = new Stack<int>();
                        PieceColor opposingSide = (currentTurn == PieceColor.White ? PieceColor.Black : PieceColor.White);

                        // Duplicate the board for each available move for the current turn, move a piece for each option, and check the opposing side to see if the king is in check.
                        compareBoard = new Board(board);
                        for (int x = 0; x < board.Tiles[i].AvailableMoves.Count; x++)
                        {
                            // Make the current turn's possible move on the duplicated board
                            BoardSupport.MovePiece(ref compareBoard, ref compareBoard.Tiles[(int)board.Tiles[i].IndexInArray], ref compareBoard.Tiles[(int)board.Tiles[i].AvailableMoves[x].IndexInArray]);
                            // Get the opposing side's moves
                            BoardSupport.GetAllSideAvailableMoves(compareBoard, opposingSide, false);
                            // For the comparison board's available moves, check if overlap between current and opposing for display
                            for (int y = 0; y < compareBoard.Tiles.Length; y++)
                            {
                                if (compareBoard.Tiles[y].Side == opposingSide)
                                {
                                    for (int z = 0; z < compareBoard.Tiles[y].AvailableMoves.Count; z++)
                                    {
                                        if (compareBoard.Tiles[y].AvailableMoves[z].IndexInArray == board.Tiles[i].AvailableMoves[x].IndexInArray)
                                        {
                                            board.OpposingMoves.Add(compareBoard.Tiles[y].AvailableMoves[z].IndexInArray);
                                        }
                                    }
                                }
                            }
                            // Set the comparison board's variables to see if the possible move would leave us in check.
                            BoardSupport.SearchForBoardVariables(compareBoard, ref compareBoard.WhiteCheck, ref compareBoard.BlackCheck, ref compareBoard.WhiteCheckmate, ref compareBoard.BlackCheckmate, ref compareBoard.Stalemate);
                            // If it does leave us in check, remove it from possible moves.
                            if ((currentTurn == PieceColor.White && compareBoard.WhiteCheck) ||
                                (currentTurn == PieceColor.Black && compareBoard.BlackCheck))
                                indexesToRemove.Push(x);//push, so that last index gets removed first
                            // And then reset the board
                            BoardSupport.MovePieceUndoLatest(ref compareBoard);
                        }
                        for (int x = indexesToRemove.Count; x > 0; x--)
                            board.Tiles[i].AvailableMoves.RemoveAt(indexesToRemove.Pop());
                    }
                }
            }

            // set the board variables
            if (ConsiderIfKingInCheck)
                BoardSupport.SearchForBoardVariables(board, ref board.WhiteCheck, ref board.BlackCheck, ref board.WhiteCheckmate, ref board.BlackCheckmate, ref board.Stalemate);
        }

        private static void GetBishopMoves(Tile tile, ref List<Tile> availableTiles)
        {
            //if (tile.Piece != PieceType.Bishop && tile.Piece != PieceType.Queen) throw new ArgumentException("Invalid piece for rule set.");
            //if (availableTiles == null) availableTiles = new List<Tile>();

            for (int i = 0; i < 6; i++)
            {
                // if we don't have a neighbor there, then skip to the next
                if (tile.Neighbors[i] == null)
                    continue;

                Stack<Tile> moveStack = new Stack<Tile>();

                // Get the top two branches
                if (i == 0)
                {
                    // Top right...
                    if (tile.Neighbors[i].Neighbors[2] != null)
                        if (tile.Neighbors[i].Neighbors[2].Piece == PieceType.None)
                            moveStack.Push(tile.Neighbors[i].Neighbors[2]);
                        else if (tile.Neighbors[i].Neighbors[2].Side != tile.Side)
                            availableTiles.Add(tile.Neighbors[i].Neighbors[2]);

                    while (moveStack.Count != 0)
                    {
                        availableTiles.Add(moveStack.Pop());
                        if (availableTiles[availableTiles.Count - 1].Neighbors[i] != null && availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[2] != null)
                            if (availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[2].Piece == PieceType.None)
                                moveStack.Push(availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[2]);
                            else if (availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[2].Side != tile.Side)
                                availableTiles.Add(availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[2]);
                    }

                    // Top left...
                    if (tile.Neighbors[i].Neighbors[3] != null)
                        if (tile.Neighbors[i].Neighbors[3].Piece == PieceType.None)
                            moveStack.Push(tile.Neighbors[i].Neighbors[3]);
                        else if (tile.Neighbors[i].Neighbors[3].Side != tile.Side)
                            availableTiles.Add(tile.Neighbors[i].Neighbors[3]);

                    while (moveStack.Count != 0)
                    {
                        availableTiles.Add(moveStack.Pop());
                        if (availableTiles[availableTiles.Count - 1].Neighbors[i] != null && availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[3] != null)
                            if (availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[3].Piece == PieceType.None)
                                moveStack.Push(availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[3]);
                            else if (availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[3].Side != tile.Side)
                                availableTiles.Add(availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[3]);
                    }
                }

                // Get the bottom left two branches
                if (i == 5)
                {
                    // Bottom...
                    if (tile.Neighbors[i].Neighbors[1] != null)
                        if (tile.Neighbors[i].Neighbors[1].Piece == PieceType.None)
                            moveStack.Push(tile.Neighbors[i].Neighbors[1]);
                        else if (tile.Neighbors[i].Neighbors[1].Side != tile.Side)
                            availableTiles.Add(tile.Neighbors[i].Neighbors[1]);

                    while (moveStack.Count != 0)
                    {
                        availableTiles.Add(moveStack.Pop());
                        if (availableTiles[availableTiles.Count - 1].Neighbors[i] != null && availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[1] != null)
                            if (availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[1].Piece == PieceType.None)
                                moveStack.Push(availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[1]);
                            else if (availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[1].Side != tile.Side)
                                availableTiles.Add(availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[1]);
                    }

                    // Top...
                    if (tile.Neighbors[i].Neighbors[3] != null)
                        if (tile.Neighbors[i].Neighbors[3].Piece == PieceType.None)
                            moveStack.Push(tile.Neighbors[i].Neighbors[3]);
                        else if (tile.Neighbors[i].Neighbors[3].Side != tile.Side)
                            availableTiles.Add(tile.Neighbors[i].Neighbors[3]);

                    while (moveStack.Count != 0)
                    {
                        availableTiles.Add(moveStack.Pop());
                        if (availableTiles[availableTiles.Count - 1].Neighbors[i] != null && availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[3] != null)
                            if (availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[3].Piece == PieceType.None)
                                moveStack.Push(availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[3]);
                            else if (availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[3].Side != tile.Side)
                                availableTiles.Add(availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[3]);
                    }
                }

                // Get the bottom right two branches
                if (i == 4)
                {
                    // Bottom...
                    if (tile.Neighbors[i].Neighbors[1] != null)
                        if (tile.Neighbors[i].Neighbors[1].Piece == PieceType.None)
                            moveStack.Push(tile.Neighbors[i].Neighbors[1]);
                        else if (tile.Neighbors[i].Neighbors[1].Side != tile.Side)
                            availableTiles.Add(tile.Neighbors[i].Neighbors[1]);

                    while (moveStack.Count != 0)
                    {
                        availableTiles.Add(moveStack.Pop());
                        if (availableTiles[availableTiles.Count - 1].Neighbors[i] != null && availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[1] != null)
                            if (availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[1].Piece == PieceType.None)
                                moveStack.Push(availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[1]);
                            else if (availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[1].Side != tile.Side)
                                availableTiles.Add(availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[1]);
                    }

                    // Top...
                    if (tile.Neighbors[i].Neighbors[2] != null)
                        if (tile.Neighbors[i].Neighbors[2].Piece == PieceType.None)
                            moveStack.Push(tile.Neighbors[i].Neighbors[2]);
                        else if (tile.Neighbors[i].Neighbors[2].Side != tile.Side)
                            availableTiles.Add(tile.Neighbors[i].Neighbors[2]);

                    while (moveStack.Count != 0)
                    {
                        availableTiles.Add(moveStack.Pop());
                        if (availableTiles[availableTiles.Count - 1].Neighbors[i] != null && availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[2] != null)
                            if (availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[2].Piece == PieceType.None)
                                moveStack.Push(availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[2]);
                            else if (availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[2].Side != tile.Side)
                                availableTiles.Add(availableTiles[availableTiles.Count - 1].Neighbors[i].Neighbors[2]);
                    }
                }
            }
        }

        private static void GetKingMoves(Tile tile, ref List<Tile> availableTiles)
        {
            //if (tile.Piece != PieceType.King) throw new ArgumentException("Invalid piece for rule set.");
            //if (availableTiles == null) availableTiles = new List<Tile>();

            // Find out every tile the other side can move into, and do not allow to be added to move set
            // 1) Cannot move into a position that the enemy can reach by moving, even a king.
            // 2) Cannot capture a piece, and thereby put oneself into check.

            // Add the orthogonal neighbors
            for (int i = 0; i < 6; i++)
                if (tile.Neighbors[i] != null)
                    if (tile.Neighbors[i].Piece == PieceType.None || tile.Neighbors[i].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[i]);

            // Add the top diagonal neighbors
            if (tile.Neighbors[0] != null)
            {
                if (tile.Neighbors[0].Neighbors[3] != null)
                    if (tile.Neighbors[0].Neighbors[3].Piece == PieceType.None || tile.Neighbors[0].Neighbors[3].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[0].Neighbors[3]);
                if (tile.Neighbors[0].Neighbors[2] != null)
                    if (tile.Neighbors[0].Neighbors[2].Piece == PieceType.None || tile.Neighbors[0].Neighbors[2].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[0].Neighbors[2]);
            }

            // Add the bottom left diagonal neighbors
            if (tile.Neighbors[5] != null)
            {
                if (tile.Neighbors[5].Neighbors[1] != null)
                    if (tile.Neighbors[5].Neighbors[1].Piece == PieceType.None || tile.Neighbors[5].Neighbors[1].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[5].Neighbors[1]);
                if (tile.Neighbors[5].Neighbors[3] != null)
                    if (tile.Neighbors[5].Neighbors[3].Piece == PieceType.None || tile.Neighbors[5].Neighbors[3].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[5].Neighbors[3]);
            }

            // Add the bottom right diagonal neighbors
            if (tile.Neighbors[4] != null)
            {
                if (tile.Neighbors[4].Neighbors[1] != null)
                    if (tile.Neighbors[4].Neighbors[1].Piece == PieceType.None || tile.Neighbors[4].Neighbors[1].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[4].Neighbors[1]);
                if (tile.Neighbors[4].Neighbors[2] != null)
                    if (tile.Neighbors[4].Neighbors[2].Piece == PieceType.None || tile.Neighbors[4].Neighbors[2].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[4].Neighbors[2]);
            }
        }

        private static void GetKnightMoves(Tile tile, ref List<Tile> availableTiles)
        {
            //if (tile.Piece != PieceType.Knight) throw new ArgumentException("Invalid piece for rule set.");
            //if (availableTiles == null) availableTiles = new List<Tile>();

            // Compare along the lines of each six orthogonal moves, with two possible options at the end
            if (tile.Neighbors[0] != null && tile.Neighbors[0].Neighbors[0] != null)
            {
                if (tile.Neighbors[0].Neighbors[0].Neighbors[2] != null)
                    if (tile.Neighbors[0].Neighbors[0].Neighbors[2].Piece == PieceType.None || tile.Neighbors[0].Neighbors[0].Neighbors[2].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[0].Neighbors[0].Neighbors[2]);
                if (tile.Neighbors[0].Neighbors[0].Neighbors[3] != null)
                    if (tile.Neighbors[0].Neighbors[0].Neighbors[3].Piece == PieceType.None || tile.Neighbors[0].Neighbors[0].Neighbors[3].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[0].Neighbors[0].Neighbors[3]);
            }
            if (tile.Neighbors[1] != null && tile.Neighbors[1].Neighbors[1] != null)
            {
                if (tile.Neighbors[1].Neighbors[1].Neighbors[4] != null)
                    if (tile.Neighbors[1].Neighbors[1].Neighbors[4].Piece == PieceType.None || tile.Neighbors[1].Neighbors[1].Neighbors[4].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[1].Neighbors[1].Neighbors[4]);
                if (tile.Neighbors[1].Neighbors[1].Neighbors[5] != null)
                    if (tile.Neighbors[1].Neighbors[1].Neighbors[5].Piece == PieceType.None || tile.Neighbors[1].Neighbors[1].Neighbors[5].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[1].Neighbors[1].Neighbors[5]);
            }
            if (tile.Neighbors[2] != null && tile.Neighbors[2].Neighbors[2] != null)
            {
                if (tile.Neighbors[2].Neighbors[2].Neighbors[0] != null)
                    if (tile.Neighbors[2].Neighbors[2].Neighbors[0].Piece == PieceType.None || tile.Neighbors[2].Neighbors[2].Neighbors[0].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[2].Neighbors[2].Neighbors[0]);
                if (tile.Neighbors[2].Neighbors[2].Neighbors[4] != null)
                    if (tile.Neighbors[2].Neighbors[2].Neighbors[4].Piece == PieceType.None || tile.Neighbors[2].Neighbors[2].Neighbors[4].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[2].Neighbors[2].Neighbors[4]);
            }
            if (tile.Neighbors[3] != null && tile.Neighbors[3].Neighbors[3] != null)
            {
                if (tile.Neighbors[3].Neighbors[3].Neighbors[0] != null)
                    if (tile.Neighbors[3].Neighbors[3].Neighbors[0].Piece == PieceType.None || tile.Neighbors[3].Neighbors[3].Neighbors[0].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[3].Neighbors[3].Neighbors[0]);
                if (tile.Neighbors[3].Neighbors[3].Neighbors[5] != null)
                    if (tile.Neighbors[3].Neighbors[3].Neighbors[5].Piece == PieceType.None || tile.Neighbors[3].Neighbors[3].Neighbors[5].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[3].Neighbors[3].Neighbors[5]);
            }
            if (tile.Neighbors[4] != null && tile.Neighbors[4].Neighbors[4] != null)
            {
                if (tile.Neighbors[4].Neighbors[4].Neighbors[2] != null)
                    if (tile.Neighbors[4].Neighbors[4].Neighbors[2].Piece == PieceType.None || tile.Neighbors[4].Neighbors[4].Neighbors[2].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[4].Neighbors[4].Neighbors[2]);
                if (tile.Neighbors[4].Neighbors[4].Neighbors[1] != null)
                    if (tile.Neighbors[4].Neighbors[4].Neighbors[1].Piece == PieceType.None || tile.Neighbors[4].Neighbors[4].Neighbors[1].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[4].Neighbors[4].Neighbors[1]);
            }
            if (tile.Neighbors[5] != null && tile.Neighbors[5].Neighbors[5] != null)
            {
                if (tile.Neighbors[5].Neighbors[5].Neighbors[3] != null)
                    if (tile.Neighbors[5].Neighbors[5].Neighbors[3].Piece == PieceType.None || tile.Neighbors[5].Neighbors[5].Neighbors[3].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[5].Neighbors[5].Neighbors[3]);
                if (tile.Neighbors[5].Neighbors[5].Neighbors[1] != null)
                    if (tile.Neighbors[5].Neighbors[5].Neighbors[1].Piece == PieceType.None || tile.Neighbors[5].Neighbors[5].Neighbors[1].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[5].Neighbors[5].Neighbors[1]);
            }
        }

        private static void GetPawnMoves(Tile tile, ref List<Tile> availableTiles, GameVariant gameVariant)
        {
            //if (tile.Piece != PieceType.Pawn) throw new ArgumentException("Invalid piece for rule set.");
            //if (availableTiles == null) availableTiles = new List<Tile>();

            tile.WasEnPassant = false;

            // Pawns can double step from any same team starting tile.
            switch (gameVariant)
            {
                // GLINSKI
                // white
                // move to neighbor[0] anytime, unoccupied neighbor[0].neighbor[0] from any same team starting hex, capture in neighbor[2] or neighbor [3]
                //black
                // move to neighbor[1] anytime, unoccupied neighbor[1].neighbor[1] from any same team starting hex, capture in neighbor[4],[5]
                // en passant: if enemy moves two forward (from starting spot) and moving just one is capturable, pawn may move to position one spot back to capture.
                case GameVariant.Glinsky:
                    switch (tile.Side)
                    {
                        case PieceColor.White:
                            // Moveable tiles
                            if (tile.Neighbors[0] != null && tile.Neighbors[0].Piece == PieceType.None)
                            {
                                availableTiles.Add(tile.Neighbors[0]);

                                if (tile.IsWhitePawnStartingTile)
                                    if (tile.Neighbors[0].Neighbors[0] != null && tile.Neighbors[0].Neighbors[0].Piece == PieceType.None)
                                        availableTiles.Add(tile.Neighbors[0].Neighbors[0]);
                            }

                            // Attackable tiles
                            if (tile.Neighbors[2] != null)
                            {
                                if (tile.Neighbors[2].Piece != PieceType.None && tile.Neighbors[2].Side != tile.Side)
                                    availableTiles.Add(tile.Neighbors[2]);
                                // Check en-passant rule
                                if (tile.Neighbors[2].Piece == PieceType.None && !tile.Neighbors[2].IsBlackPawnStartingTile && tile.Neighbors[4] != null && tile.Neighbors[4].Piece == PieceType.Pawn && tile.Neighbors[4].Side != tile.Side && tile.Neighbors[4].PieceWasPawnOnStartingTile && tile.Neighbors[4].PieceHasMoved)
                                {
                                    availableTiles.Add(tile.Neighbors[2]);
                                    tile.WasEnPassant = true;
                                    tile.EnPassantIndex = tile.Neighbors[4].IndexInArray;
                                }
                            }
                            if (tile.Neighbors[3] != null)
                            {
                                if (tile.Neighbors[3].Piece != PieceType.None && tile.Neighbors[3].Side != tile.Side)
                                    availableTiles.Add(tile.Neighbors[3]);
                                // Check en-passant rule
                                if (tile.Neighbors[3].Piece == PieceType.None && !tile.Neighbors[3].IsBlackPawnStartingTile && tile.Neighbors[5] != null && tile.Neighbors[5].Piece == PieceType.Pawn && tile.Neighbors[5].Side != tile.Side && tile.Neighbors[5].PieceWasPawnOnStartingTile && tile.Neighbors[5].PieceHasMoved)
                                {
                                    availableTiles.Add(tile.Neighbors[3]);
                                    tile.WasEnPassant = true;
                                    tile.EnPassantIndex = tile.Neighbors[5].IndexInArray;
                                }
                            }
                            break;
                        case PieceColor.Black:
                            // Moveable tiles
                            if (tile.Neighbors[1] != null && tile.Neighbors[1].Piece == PieceType.None)
                            {
                                availableTiles.Add(tile.Neighbors[1]);

                                if (tile.IsBlackPawnStartingTile)
                                    if (tile.Neighbors[1].Neighbors[1] != null && tile.Neighbors[1].Neighbors[1].Piece == PieceType.None)
                                        availableTiles.Add(tile.Neighbors[1].Neighbors[1]);
                            }

                            // Attackable tiles
                            if (tile.Neighbors[4] != null)
                            {
                                if (tile.Neighbors[4].Piece != PieceType.None && tile.Neighbors[4].Side != tile.Side)
                                    availableTiles.Add(tile.Neighbors[4]);
                                // Check en-passant rule
                                if (tile.Neighbors[4].Piece == PieceType.None && !tile.Neighbors[4].IsWhitePawnStartingTile && tile.Neighbors[2] != null && tile.Neighbors[2].Piece == PieceType.Pawn && tile.Neighbors[2].Side != tile.Side && tile.Neighbors[2].PieceWasPawnOnStartingTile && tile.Neighbors[2].PieceHasMoved)
                                {
                                    availableTiles.Add(tile.Neighbors[4]);
                                    tile.WasEnPassant = true;
                                    tile.EnPassantIndex = tile.Neighbors[2].IndexInArray;
                                }
                            }
                            if (tile.Neighbors[5] != null)
                            {
                                if (tile.Neighbors[5].Piece != PieceType.None && tile.Neighbors[5].Side != tile.Side)
                                    availableTiles.Add(tile.Neighbors[5]);
                                // Check en-passant rule
                                if (tile.Neighbors[5].Piece == PieceType.None && !tile.Neighbors[5].IsWhitePawnStartingTile && tile.Neighbors[3] != null && tile.Neighbors[3].Piece == PieceType.Pawn && tile.Neighbors[3].Side != tile.Side && tile.Neighbors[3].PieceWasPawnOnStartingTile && tile.Neighbors[3].PieceHasMoved)
                                {
                                    availableTiles.Add(tile.Neighbors[5]);
                                    tile.WasEnPassant = true;
                                    tile.EnPassantIndex = tile.Neighbors[3].IndexInArray;
                                }
                            }
                            break;
                    }
                    break;
                // MCCOOEY
                // white
                // move to neighbor[0] anytime, unoccupied neighbor[0].neighbor[0] from any same team starting hex EXCEPT FILE F, capture in neighbor[0].neighbor[2] or neighbor[0].neighbor[3]
                // black
                // move to neighbor[1] anytime, unoccupied neighbor[1].neighbor[1] from any same team starting hex EXCEPT FILE F, capture in neighbor[1].neighbor[4] or neighbor[1].neighbor[5]
                // en passant: if enemy moves two forward (from starting spot) and moving just one is capturable, pawn may move to position one spot back to capture.
                case GameVariant.McCooey:
                    switch (tile.Side)
                    {
                        case PieceColor.White:
                            if (tile.Neighbors[0] != null && tile.Neighbors[0].Piece == PieceType.None)
                            {
                                // Moveable tiles
                                availableTiles.Add(tile.Neighbors[0]);

                                if (tile.IsWhitePawnStartingTile && tile.file != File.F && tile.Neighbors[0].Neighbors[0] != null && tile.Neighbors[0].Neighbors[0].Piece == PieceType.None)
                                    availableTiles.Add(tile.Neighbors[0].Neighbors[0]);

                                // Attackable tiles
                                if (tile.Neighbors[0].Neighbors[2] != null)
                                {
                                    if (tile.Neighbors[0].Neighbors[2].Piece != PieceType.None && tile.Neighbors[0].Neighbors[2].Side != tile.Side)
                                        availableTiles.Add(tile.Neighbors[0].Neighbors[2]);
                                    // Check en-passant rule
                                    if (tile.Neighbors[0].Neighbors[2].Piece == PieceType.None && !tile.Neighbors[0].Neighbors[2].IsBlackPawnStartingTile && tile.Neighbors[2] != null && tile.Neighbors[2].Piece == PieceType.Pawn && tile.Neighbors[2].Side != tile.Side && tile.Neighbors[2].PieceWasPawnOnStartingTile && tile.Neighbors[2].PieceHasMoved)
                                    {
                                        availableTiles.Add(tile.Neighbors[0].Neighbors[2]);
                                        tile.WasEnPassant = true;
                                        tile.EnPassantIndex = tile.Neighbors[2].IndexInArray;
                                    }
                                }
                                if (tile.Neighbors[0].Neighbors[3] != null)
                                {
                                    if (tile.Neighbors[0].Neighbors[3].Piece != PieceType.None && tile.Neighbors[0].Neighbors[3].Side != tile.Side)
                                        availableTiles.Add(tile.Neighbors[0].Neighbors[3]);
                                    // Check en-passant rule
                                    if (tile.Neighbors[0].Neighbors[3].Piece == PieceType.None && !tile.Neighbors[0].Neighbors[3].IsBlackPawnStartingTile && tile.Neighbors[3] != null && tile.Neighbors[3].Piece == PieceType.Pawn && tile.Neighbors[3].Side != tile.Side && tile.Neighbors[3].PieceWasPawnOnStartingTile && tile.Neighbors[3].PieceHasMoved)
                                    {
                                        availableTiles.Add(tile.Neighbors[0].Neighbors[3]);
                                        tile.WasEnPassant = true;
                                        tile.EnPassantIndex = tile.Neighbors[3].IndexInArray;
                                    }
                                }
                            }
                            break;
                        case PieceColor.Black:
                            if (tile.Neighbors[1] != null && tile.Neighbors[1].Piece == PieceType.None)
                            {
                                // Moveable tiles
                                availableTiles.Add(tile.Neighbors[1]);

                                if (tile.IsBlackPawnStartingTile && tile.file != File.F && tile.Neighbors[1].Neighbors[1] != null && tile.Neighbors[1].Neighbors[1].Piece == PieceType.None)
                                    availableTiles.Add(tile.Neighbors[1].Neighbors[1]);

                                // Attackable tiles
                                if (tile.Neighbors[1].Neighbors[4] != null)
                                {
                                    if (tile.Neighbors[1].Neighbors[4].Piece != PieceType.None && tile.Neighbors[1].Neighbors[4].Side != tile.Side)
                                        availableTiles.Add(tile.Neighbors[1].Neighbors[4]);
                                    // Check en-passant rule
                                    if (tile.Neighbors[1].Neighbors[4].Piece == PieceType.None && !tile.Neighbors[1].Neighbors[4].IsWhitePawnStartingTile && tile.Neighbors[4] != null && tile.Neighbors[4].Piece == PieceType.Pawn && tile.Neighbors[4].Side != tile.Side && tile.Neighbors[4].PieceWasPawnOnStartingTile && tile.Neighbors[4].PieceHasMoved)
                                    {
                                        availableTiles.Add(tile.Neighbors[1].Neighbors[4]);
                                        tile.WasEnPassant = true;
                                        tile.EnPassantIndex = tile.Neighbors[4].IndexInArray;
                                    }
                                }
                                if (tile.Neighbors[1].Neighbors[5] != null)
                                {
                                    if (tile.Neighbors[1].Neighbors[5].Piece != PieceType.None && tile.Neighbors[1].Neighbors[5].Side != tile.Side)
                                        availableTiles.Add(tile.Neighbors[1].Neighbors[5]);
                                    // Check en-passant rule
                                    if (tile.Neighbors[1].Neighbors[5].Piece == PieceType.None && !tile.Neighbors[1].Neighbors[5].IsWhitePawnStartingTile && tile.Neighbors[5] != null && tile.Neighbors[5].Piece == PieceType.Pawn && tile.Neighbors[5].Side != tile.Side && tile.Neighbors[5].PieceWasPawnOnStartingTile && tile.Neighbors[5].PieceHasMoved)
                                    {
                                        availableTiles.Add(tile.Neighbors[1].Neighbors[5]);
                                        tile.WasEnPassant = true;
                                        tile.EnPassantIndex = tile.Neighbors[5].IndexInArray;
                                    }
                                }
                            }
                            break;
                    }
                    break;
                // SHAFRAN
                // Same as McCooey, except first move: pawn may go to middle of their file. 
                // Therefore, a and i pawns can go one, and d/e/f pawns may go single/double/triple, others single/double. En passant rules apply, up to two possible locations if a triple move.
                case GameVariant.Shafran:
                    switch (tile.Side)
                    {
                        case PieceColor.White:
                            // Moveable tiles
                            if (tile.Neighbors[0] != null && tile.Neighbors[0].Piece == PieceType.None)
                            {
                                availableTiles.Add(tile.Neighbors[0]);

                                // If we are on a starting tile, all pieces can move at least one space towards the middle of the file. 
                                // DEF file can move up to 3.
                                if (tile.IsWhitePawnStartingTile && tile.file != File.A && tile.file != File.I)
                                {
                                    if (tile.Neighbors[0].Neighbors[0] != null && tile.Neighbors[0].Neighbors[0].Piece == PieceType.None)
                                    {
                                        availableTiles.Add(tile.Neighbors[0].Neighbors[0]);

                                        if (tile.file == File.D || tile.file == File.E || tile.file == File.F)
                                            if (tile.Neighbors[0].Neighbors[0].Neighbors[0] != null && tile.Neighbors[0].Neighbors[0].Neighbors[0].Piece == PieceType.None)
                                                availableTiles.Add(tile.Neighbors[0].Neighbors[0].Neighbors[0]);
                                    }
                                }

                                // Attackable tiles
                                if (tile.Neighbors[0].Neighbors[2] != null)
                                {
                                    if (tile.Neighbors[0].Neighbors[2].Piece != PieceType.None && tile.Neighbors[0].Neighbors[2].Side != tile.Side)
                                        availableTiles.Add(tile.Neighbors[0].Neighbors[2]);
                                    // Check en-passant rule
                                    if (tile.Neighbors[0].Neighbors[2].Piece == PieceType.None && tile.Neighbors[2] != null && !tile.Neighbors[0].Neighbors[2].IsBlackPawnStartingTile)
                                    {
                                        if (tile.Neighbors[2].Piece == PieceType.Pawn && tile.Neighbors[2].Side != tile.Side && tile.Neighbors[2].PieceWasPawnOnStartingTile && tile.Neighbors[2].PieceHasMoved)
                                        {
                                            availableTiles.Add(tile.Neighbors[0].Neighbors[2]);
                                            tile.WasEnPassant = true;
                                            tile.EnPassantIndex = tile.Neighbors[2].IndexInArray;
                                        }
                                        if (tile.Neighbors[4] != null && tile.Neighbors[4].Piece == PieceType.Pawn && tile.Neighbors[4].Side != tile.Side && tile.Neighbors[4].PieceWasPawnOnStartingTile && tile.Neighbors[4].PieceHasMoved)
                                        {
                                            availableTiles.Add(tile.Neighbors[0].Neighbors[2]);
                                            tile.WasEnPassant = true;
                                            tile.EnPassantIndex = tile.Neighbors[4].IndexInArray;
                                        }
                                    }
                                }
                                if (tile.Neighbors[0].Neighbors[3] != null)
                                {
                                    if (tile.Neighbors[0].Neighbors[3].Piece != PieceType.None && tile.Neighbors[0].Neighbors[3].Side != tile.Side)
                                        availableTiles.Add(tile.Neighbors[0].Neighbors[3]);
                                    // Check en-passant rule
                                    if (tile.Neighbors[0].Neighbors[3].Piece == PieceType.None && tile.Neighbors[3] != null && !tile.Neighbors[0].Neighbors[3].IsBlackPawnStartingTile)
                                    {
                                        if (tile.Neighbors[3].Piece == PieceType.Pawn && tile.Neighbors[3].Side != tile.Side && tile.Neighbors[3].PieceWasPawnOnStartingTile && tile.Neighbors[3].PieceHasMoved)
                                        {
                                            availableTiles.Add(tile.Neighbors[0].Neighbors[3]);
                                            tile.WasEnPassant = true;
                                            tile.EnPassantIndex = tile.Neighbors[3].IndexInArray;
                                        }
                                        if (tile.Neighbors[5] != null && tile.Neighbors[5].Piece == PieceType.Pawn && tile.Neighbors[5].Side != tile.Side && tile.Neighbors[5].PieceWasPawnOnStartingTile && tile.Neighbors[5].PieceHasMoved)
                                        {
                                            availableTiles.Add(tile.Neighbors[0].Neighbors[3]);
                                            tile.WasEnPassant = true;
                                            tile.EnPassantIndex = tile.Neighbors[5].IndexInArray;
                                        }
                                    }
                                }
                            }
                            break;
                        case PieceColor.Black:
                            // Moveable tiles
                            if (tile.Neighbors[1] != null && tile.Neighbors[1].Piece == PieceType.None)
                            {
                                availableTiles.Add(tile.Neighbors[1]);

                                // If we are on a starting tile, all pieces can move at least one space towards the middle of the file. 
                                // DEF file can move up to 3.
                                if (tile.IsBlackPawnStartingTile && tile.file != File.A && tile.file != File.I)
                                {
                                    if (tile.Neighbors[1].Neighbors[1] != null && tile.Neighbors[1].Neighbors[1].Piece == PieceType.None)
                                    {
                                        availableTiles.Add(tile.Neighbors[1].Neighbors[1]);

                                        if (tile.file == File.D || tile.file == File.E || tile.file == File.F)
                                            if (tile.Neighbors[1].Neighbors[1].Neighbors[1] != null && tile.Neighbors[1].Neighbors[1].Neighbors[1].Piece == PieceType.None)
                                                availableTiles.Add(tile.Neighbors[1].Neighbors[1].Neighbors[1]);
                                    }
                                }

                                // Attackable tiles
                                if (tile.Neighbors[1].Neighbors[4] != null)
                                {
                                    if (tile.Neighbors[1].Neighbors[4].Piece != PieceType.None && tile.Neighbors[1].Neighbors[4].Side != tile.Side)
                                        availableTiles.Add(tile.Neighbors[1].Neighbors[4]);
                                    // Check en-passant rule
                                    if (tile.Neighbors[1].Neighbors[4].Piece == PieceType.None && tile.Neighbors[4] != null && !tile.Neighbors[1].Neighbors[4].IsWhitePawnStartingTile)
                                    {
                                        if (tile.Neighbors[4].Piece == PieceType.Pawn && tile.Neighbors[4].Side != tile.Side && tile.Neighbors[4].PieceWasPawnOnStartingTile && tile.Neighbors[4].PieceHasMoved)
                                        {
                                            availableTiles.Add(tile.Neighbors[1].Neighbors[4]);
                                            tile.WasEnPassant = true;
                                            tile.EnPassantIndex = tile.Neighbors[4].IndexInArray;
                                        }
                                        if (tile.Neighbors[2] != null && tile.Neighbors[2].Piece == PieceType.Pawn && tile.Neighbors[2].Side != tile.Side && tile.Neighbors[2].PieceWasPawnOnStartingTile && tile.Neighbors[2].PieceHasMoved)
                                        {
                                            availableTiles.Add(tile.Neighbors[1].Neighbors[4]);
                                            tile.WasEnPassant = true;
                                            tile.EnPassantIndex = tile.Neighbors[2].IndexInArray;
                                        }
                                    }
                                }
                                if (tile.Neighbors[1].Neighbors[5] != null)
                                {
                                    if (tile.Neighbors[1].Neighbors[5].Piece != PieceType.None && tile.Neighbors[1].Neighbors[5].Side != tile.Side)
                                        availableTiles.Add(tile.Neighbors[1].Neighbors[5]);
                                    // Check en-passant rule
                                    if (tile.Neighbors[1].Neighbors[5].Piece == PieceType.None && tile.Neighbors[5] != null && !tile.Neighbors[1].Neighbors[5].IsWhitePawnStartingTile)
                                    {
                                        if (tile.Neighbors[5].Piece == PieceType.Pawn && tile.Neighbors[5].Side != tile.Side && tile.Neighbors[5].PieceWasPawnOnStartingTile && tile.Neighbors[5].PieceHasMoved)
                                        {
                                            availableTiles.Add(tile.Neighbors[1].Neighbors[5]);
                                            tile.WasEnPassant = true;
                                            tile.EnPassantIndex = tile.Neighbors[5].IndexInArray;
                                        }
                                        if (tile.Neighbors[3] != null && tile.Neighbors[3].Piece == PieceType.Pawn && tile.Neighbors[3].Side != tile.Side && tile.Neighbors[3].PieceWasPawnOnStartingTile && tile.Neighbors[3].PieceHasMoved)
                                        {
                                            availableTiles.Add(tile.Neighbors[1].Neighbors[5]);
                                            tile.WasEnPassant = true;
                                            tile.EnPassantIndex = tile.Neighbors[3].IndexInArray;
                                        }
                                    }
                                }
                            }
                            break;
                    }
                    break;
            }
        }

        private static void GetQueenMoves(Tile tile, ref List<Tile> availableTiles)
        {
            //if (tile.Piece != PieceType.Queen) throw new ArgumentException("Invalid piece for rule set.");
            //if (availableTiles == null) availableTiles = new List<Tile>();

            GetBishopMoves(tile, ref availableTiles);
            GetRookMoves(tile, ref availableTiles);
        }

        private static void GetRookMoves(Tile tile, ref List<Tile> availableTiles)
        {
            //if (tile.Piece != PieceType.Rook && tile.Piece != PieceType.Queen) throw new ArgumentException("Invalid piece for rule set.");
            //if (availableTiles == null) availableTiles = new List<Tile>();

            for (int i = 0; i < 6; i++)
            {
                Stack<Tile> moveStack = new Stack<Tile>();

                // Check and push immediate neighbors for move/attack
                if (tile.Neighbors[i] != null)
                {
                    if (tile.Neighbors[i].Piece == PieceType.None)
                        moveStack.Push(tile.Neighbors[i]);
                    else if (tile.Neighbors[i].Side != tile.Side)
                        availableTiles.Add(tile.Neighbors[i]);
                }

                // Iterate through move stack
                while (moveStack.Count != 0)
                {
                    availableTiles.Add(moveStack.Pop());
                    if (availableTiles[availableTiles.Count - 1].Neighbors[i] != null)
                    {
                        if (availableTiles[availableTiles.Count - 1].Neighbors[i].Piece == PieceType.None)
                            moveStack.Push(availableTiles[availableTiles.Count - 1].Neighbors[i]);
                        else if (availableTiles[availableTiles.Count - 1].Neighbors[i].Side != tile.Side)
                            availableTiles.Add(availableTiles[availableTiles.Count - 1].Neighbors[i]);
                    }
                }
            }
        }

        /// <summary>
        /// Returns whether a king can castle to the queen side of the board.
        /// </summary>
        /// <param name="board">The board to inspect</param>
        /// <param name="kingSide">Which color the king is, white or black.</param>
        /// <param name="LongCastle">If true, long castle; if false, short castle</param>
        /// <returns></returns>
        public static void CanCastleQueenSide(Board board, PieceColor kingSide, ref short QueenCastle)
        {
            QueenCastle = 0;
            switch (kingSide)
            {
                case PieceColor.White:
                    // Check the king/rook hasn't moved, is not in check, and will not cross in check.
                    if (board.WhiteKing == 30 && board.Tiles[30].PieceHasMoved == false &&                      // Check king is in center and hasn't moved
                        board.Tiles[0].Piece == PieceType.Rook && board.Tiles[0].PieceHasMoved == false &&      // Check rook is on flank and hasn't moved
                        board.Tiles[6].Piece == PieceType.None && board.Tiles[13].Piece == PieceType.None && 
                        board.Tiles[21].Piece == PieceType.None && !board.WhiteCheck &&
                        !IsKingInCheck(board, kingSide, board.Tiles[21]) && !IsKingInCheck(board, kingSide, board.Tiles[13]))
                    {
                        QueenCastle++;
                        if (!IsKingInCheck(board, kingSide, board.Tiles[6]))
                        {
                            QueenCastle++;
                        }
                    }
                    break;
                case PieceColor.Black:
                    // Check the king/rook hasn't moved, is not in check, and will not cross in check.
                    if (board.BlackKing == 39 && board.Tiles[39].PieceHasMoved == false &&                      // Check king is in center and hasn't moved
                        board.Tiles[69].Piece == PieceType.Rook && board.Tiles[69].PieceHasMoved == false &&    // Check rook is on flank and hasn't moved
                        board.Tiles[48].Piece == PieceType.None && board.Tiles[56].Piece == PieceType.None &&
                        board.Tiles[63].Piece == PieceType.None && !board.BlackCheck &&
                        !IsKingInCheck(board, kingSide, board.Tiles[48]) && !IsKingInCheck(board, kingSide, board.Tiles[56]))
                    {
                        QueenCastle++;
                        if (!IsKingInCheck(board, kingSide, board.Tiles[63]))
                        {
                            QueenCastle++;
                        }
                    }
                    break;
            }
        }

        /// <summary>
        /// Returns whether a king can castle to the bishop side of the board.
        /// </summary>
        /// <param name="board">The board to inspect</param>
        /// <param name="side">Which color the king is, white or black.</param>
        /// <param name="LongCastle">If true, long castle; if false, short castle</param>
        /// <returns></returns>
        public static void CanCastleBishopSide(Board board, PieceColor kingSide, ref short BishopCastle)
        {
            BishopCastle = 0;
            switch (kingSide)
            {
                case PieceColor.White:
                    // Check the king/rook hasn't moved, is not in check, and will not cross in check.
                    if (board.WhiteKing == 30 && board.Tiles[30].PieceHasMoved == false &&                      // Check king is in center and hasn't moved
                        board.Tiles[64].Piece == PieceType.Rook && board.Tiles[64].PieceHasMoved == false &&      // Check rook is on flank and hasn't moved
                        board.Tiles[40].Piece == PieceType.None && board.Tiles[49].Piece == PieceType.None &&
                        board.Tiles[57].Piece == PieceType.None && !board.WhiteCheck &&
                        !IsKingInCheck(board, kingSide, board.Tiles[40]) && !IsKingInCheck(board, kingSide, board.Tiles[49]))
                    {
                        BishopCastle++;
                        if (!IsKingInCheck(board, kingSide, board.Tiles[57]))
                        {
                            BishopCastle++;
                        }
                    }
                    break;
                case PieceColor.Black:
                    // Check the king/rook hasn't moved, is not in check, and will not cross in check.
                    if (board.BlackKing == 39 && board.Tiles[39].PieceHasMoved == false &&                      // Check king is in center and hasn't moved
                        board.Tiles[5].Piece == PieceType.Rook && board.Tiles[5].PieceHasMoved == false &&    // Check rook is on flank and hasn't moved
                        board.Tiles[12].Piece == PieceType.None && board.Tiles[20].Piece == PieceType.None &&
                        board.Tiles[29].Piece == PieceType.None && !board.BlackCheck &&
                        !IsKingInCheck(board, kingSide, board.Tiles[29]) && !IsKingInCheck(board, kingSide, board.Tiles[20]))
                    {
                        BishopCastle++;
                        if (!IsKingInCheck(board, kingSide, board.Tiles[12]))
                        {
                            BishopCastle++;
                        }
                    }
                    break;
            }
        }

        /// <summary>
        /// Disover and check if the king would be in check in the tile.
        /// </summary>
        /// <param name="tile">The desired tile to check.</param>
        /// <returns></returns>
        /// TODO when using to check if castling, pass board with all moves already discovered, rather than rediscover for each tile.
        public static bool IsKingInCheck(Board board, PieceColor kingSide, Tile tile)
        {
            // Determine the opposing side
            PieceColor opposingSide = (kingSide == PieceColor.White ? PieceColor.Black : PieceColor.White);

            // Get opposing side available moves, not checking whether king will be in check.
            GetAllSideAvailableMoves(board, opposingSide, false);

            // Only need to check moveable tiles
            for (int i = 0; i < board.Tiles.Length; i++)
            {
                if (board.Tiles[i].Piece != PieceType.None && board.Tiles[i].Side == opposingSide)
                {
                    if (board.Tiles[i].Piece == PieceType.Pawn)
                    {
                        if (board.Tiles[i].AvailableMoves.Contains(tile) && tile.file != board.Tiles[i].file)
                        {
                            return true;
                        }
                    }
                    else if (board.Tiles[i].AvailableMoves.Contains(tile))
                    {
                            return true;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Move a piece, updating the board and tiles.
        /// </summary>
        /// <param name="board">The board to update.</param>
        /// <param name="SourceTile">The tile to move the piece from.</param>
        /// <param name="DestTile">The destination tile to move the piece to.</param>
        /// <param name="ClearSourceAvailableMoves">Set to "false" if maintaining them to immediately unmove (i.e. alpha beta),
        /// true if we are making a regular move, not in AI.</param>
        /// <remarks>TODO ref only necessary board info, don't copy entire board.
        /// TODO when calling in AB, get moves at start, before piece loop, and don't call again.</remarks>
        public static void MovePiece(ref Board board, ref Tile SourceTile, ref Tile DestTile)//, bool ClearSourceAvailableMoves)
        {
            // Record the move history. Moves are tracked before the move is made.
            // Set all variables, such as counts, enpassant file/rank, etc, in order to unmove a piece later.
            // TODO for future AI, track castling ability
            Move m = new Move(
                // Movement and piece info.
                SourceTile.Piece, SourceTile.Side, SourceTile.IndexInArray, DestTile.IndexInArray,
                // CAPTURE: these can be null or none, replace attacked pieces if direct attack. Always opposite side of source side.
                DestTile.Piece,
                // CAPTURE: Enpassant variables, replace if necessary. Always a pawn of the opposite side of source side.
                SourceTile.WasEnPassant, SourceTile.EnPassantIndex, 
                // PIECE:
                SourceTile.PieceHasMoved, SourceTile.PieceWasPawnOnStartingTile,
                // todo we don't need to track these? This is because we only look at the "current turn" pieces, and opposing side pieces aren't up to date anyways.
                //      HOWEVER, if we do, we need to assign all these + enpassant to the undo move functions
                //DestTile.PieceHasMoved, DestTile.PieseWasPawnOnStartingTile,
                // BOARD: variables
                board.RepeatedMoveCount, board.FiftyMoveCount, 0);

            // Add the move to the history
            board.Moves.Add(m);

            // Set fifty move counts
            if (DestTile.Piece == PieceType.None && SourceTile.Piece != PieceType.Pawn)
                board.FiftyMoveCount++;
            else
                board.FiftyMoveCount = 0;

            // Capture pieces from opposing sides
            if (DestTile.Piece != PieceType.None)
            {
                //board.BoardPositions.Clear();
                if (DestTile.Side == PieceColor.White)
                    board.WhitePiecesDead.Add(DestTile.Piece);
                else
                    board.BlackPiecesDead.Add(DestTile.Piece);
            }
            else if (SourceTile.Piece == PieceType.Pawn && SourceTile.WasEnPassant && SourceTile.file != DestTile.file)
            {
                //board.BoardPositions.Clear();
                if (SourceTile.Side == PieceColor.Black)
                    board.WhitePiecesDead.Add(PieceType.Pawn);
                else
                    board.BlackPiecesDead.Add(PieceType.Pawn);
                board.Tiles[SourceTile.EnPassantIndex].Piece = PieceType.None;
            }

            // Pawn:
            // if a piece has moved and WasOnStartingTile = true, then it is a pawn just moved from it's starting location on it's second move.
            //      then this move should set WasOnStartingTile = false, and cannot be captured en passant.
            if (SourceTile.Piece == PieceType.Pawn && SourceTile.PieceHasMoved && SourceTile.PieceWasPawnOnStartingTile)
                DestTile.PieceWasPawnOnStartingTile = false;
            else
                DestTile.PieceWasPawnOnStartingTile = true;//temp, move elsewhere, more efficient?

            // Set destination tile variables. Do not need to clear available moves as moves are cleared/overwritten on GetAvailableMoves
            DestTile.PieceHasMoved = true;
            DestTile.Piece = SourceTile.Piece;
            DestTile.Side = SourceTile.Side;
            DestTile.WasEnPassant = false;
            DestTile.EnPassantIndex = -1;
            DestTile.PieceHasMoved = true;

            // Clear source tile info. TODO Available moves clear to free up memory. Does this affect undo move?
            SourceTile.Piece = PieceType.None;
            SourceTile.WasEnPassant = false;
            SourceTile.EnPassantIndex = -1;
            //if (ClearSourceAvailableMoves) 
            //SourceTile.AvailableMoves.Clear();  

            // Update king pointers
            if (DestTile.Piece == PieceType.King)
            {
                if (DestTile.Side == PieceColor.White)
                    board.WhiteKing = DestTile.IndexInArray;
                else
                    board.BlackKing = DestTile.IndexInArray;
            }

            // Set repeated moves
            string boardLayout = "";
            for (int i = 0; i < board.Tiles.Length; i++)
            {
                if (board.Tiles[i].Piece != PieceType.None)
                {
                    boardLayout += i.ToString() + board.Tiles[i].Side.ToString().Substring(0, 1) + board.Tiles[i].Piece.ToString().Substring(0, 2);
                    if (board.Tiles[i].Piece == PieceType.Pawn && board.Tiles[i].WasEnPassant)
                        boardLayout += "E";
                }
            }
            board.BoardPositions.Add(boardLayout);
            board.RepeatedMoveCount = 0;
            for (int i = 0; i < board.BoardPositions.Count; i++)
                if (board.BoardPositions[i] == boardLayout)
                    board.RepeatedMoveCount++;
        }

        public static void MovePieceUndoLatest(ref Board board)
        {
            if (board.Moves.Count == 0)
                return;

            // the opposite of movepiece, reset all variables the way they were.
            // Get the latest move
            // TODO for future AI, track castling ability
            Move m = board.Moves[board.Moves.Count - 1];
            board.Moves.RemoveAt(board.Moves.Count - 1);
            PieceColor opposingSide = (m.SourceTileSide == PieceColor.White ? PieceColor.Black : PieceColor.White);

            // Moves are stored as the piece goes from SourceTile -> DestTile.
            // To get the back, we need to reset SourceTile <- DestTile by resetting SourceTile variables

            // Board variables
            if (board.BoardPositions.Count > 0)
                board.BoardPositions.RemoveAt(board.BoardPositions.Count - 1);
            board.RepeatedMoveCount = m.BoardRepeatCount;
            board.FiftyMoveCount = m.BoardFiftyMoveCount;
            // Update king pointers
            if (m.SourceTilePiece == PieceType.King)
            {
                if (m.SourceTileSide == PieceColor.White)
                    board.WhiteKing = m.SourceTileIndex;
                else
                    board.BlackKing = m.SourceTileIndex;
            }

            // Move the destination tile info to the source tile info.
            board.Tiles[m.SourceTileIndex].Piece = m.SourceTilePiece;
            board.Tiles[m.SourceTileIndex].Side = m.SourceTileSide;
            board.Tiles[m.SourceTileIndex].PieceHasMoved = m.SourceTilePieceHasMoved;
            board.Tiles[m.SourceTileIndex].PieceWasPawnOnStartingTile = m.SourceTilePieceWasPawnOnStartingTile;
            board.Tiles[m.DestTileIndex].Piece = m.DestTilePiece;
            board.Tiles[m.DestTileIndex].Side = opposingSide;
            if (m.SourceTilePiece == PieceType.Pawn && m.DestTilePiece == PieceType.None && m.WasEnPassant && board.Tiles[m.SourceTileIndex].file != board.Tiles[m.DestTileIndex].file)
            {
                board.Tiles[m.SourceTileIndex].WasEnPassant = true;
                board.Tiles[m.SourceTileIndex].EnPassantIndex = m.EnPassantIndex;
                board.Tiles[m.EnPassantIndex].Piece = PieceType.Pawn;
                board.Tiles[m.EnPassantIndex].Side = opposingSide;

                switch (opposingSide)
                {
                    case PieceColor.White:
                        board.WhitePiecesDead.RemoveAt(board.WhitePiecesDead.Count - 1);
                        break;
                    case PieceColor.Black:
                        board.BlackPiecesDead.RemoveAt(board.BlackPiecesDead.Count - 1);
                        break;
                }
            }
            else
            {
                if (m.DestTilePiece != PieceType.None)
                {
                    switch (opposingSide)
                    {
                        case PieceColor.White:
                            board.WhitePiecesDead.RemoveAt(board.WhitePiecesDead.Count - 1);
                            break;
                        case PieceColor.Black:
                            board.BlackPiecesDead.RemoveAt(board.BlackPiecesDead.Count - 1);
                            break;
                    }
                }
            }

            //board.CurrentTurn = (board.CurrentTurn == PieceColor.White ? PieceColor.Black : PieceColor.White);
        }

        public static void EndTurn(ref Board board, PieceType type)
        {
            board.CurrentTurn = (board.CurrentTurn == PieceColor.White ? PieceColor.Black : PieceColor.White);

            //upgrade pawns
            if (type != PieceType.None) UpgradePawns(ref board, type);

            // Get current turns available moves, including king check
            BoardSupport.GetAllSideAvailableMoves(board, board.CurrentTurn, true);

            // Check castling
            if (board.Variant == GameVariant.Shafran)
            {
                switch (board.CurrentTurn)
                {
                    case PieceColor.White:
                        BoardSupport.CanCastleBishopSide(board, PieceColor.White, ref board.CanCastleWhiteBishop);
                        BoardSupport.CanCastleQueenSide(board, PieceColor.White, ref board.CanCastleWhiteQueen);
                        break;
                    case PieceColor.Black:
                        BoardSupport.CanCastleBishopSide(board, PieceColor.Black, ref board.CanCastleBlackBishop);
                        BoardSupport.CanCastleQueenSide(board, PieceColor.Black, ref board.CanCastleBlackQueen);
                        break;
                }
            }

            // Since we cannot end our turn with king in check, and checkmate/stalemate end the game, we
            // do not need to investigate available moves for the side ending, only the side who's turn it will now be.
            if (board.CurrentTurn == PieceColor.White)
                board.BlackCheck = false;
            else
                board.WhiteCheck = false;
        }

        public static void SearchForBoardVariables(Board board, ref bool WhiteCheck, ref bool BlackCheck, ref bool WhiteMate, ref bool BlackMate, ref bool Stalemate)
        {
            //-------------------------------------------------
            // check if the king is in check
            //-------------------------------------------------
            PieceColor opposingSide = (board.CurrentTurn == PieceColor.White ? PieceColor.Black : PieceColor.White);

            GetAllSideAvailableMoves(board, opposingSide, false);
            BlackCheck = WhiteCheck = false;
            for (int i = 0; i < board.Tiles.Length; i++)
            {
                if (board.Tiles[i] != null && board.Tiles[i].Piece != PieceType.None && board.Tiles[i].Side == opposingSide)
                {
                    switch (opposingSide)
                    {
                        case PieceColor.White:
                            if (board.Tiles[i].AvailableMoves.Contains(board.Tiles[board.BlackKing]))
                            {
                                BlackCheck = true;
                                i = board.Tiles.Length;
                            }
                            //else
                            //    BlackCheck = false;
                            break;
                        case PieceColor.Black:
                            if (board.Tiles[i].AvailableMoves.Contains(board.Tiles[board.WhiteKing]))
                            {
                                WhiteCheck = true;
                                i = board.Tiles.Length;
                            }
                            //else
                            //    WhiteCheck = false;
                            break;
                    }
                }
            }

            //-------------------------------------------------
            // check if the king is in checkmate/stalemate
            // We've already at this point calculated our current turn's available moves, so we can just look at the count
            // If in check, we can't be stalemated.
            //-------------------------------------------------
            Stalemate = true;
            if (WhiteCheck) { WhiteMate = true; Stalemate = false; }
            if (BlackCheck) { BlackMate = true; Stalemate = false; }
            
            for (int i = 0; i < board.Tiles.Length; i++)
            {
                if (board.Tiles[i] != null && board.Tiles[i].Piece != PieceType.None && board.Tiles[i].Side == board.CurrentTurn)
                {
                    switch (board.CurrentTurn)
                    {
                        case PieceColor.White:
                            
                            if (board.Tiles[i].AvailableMoves.Count > 0)
                            {
                                WhiteMate = false;
                                Stalemate = false;
                                i = board.Tiles.Length;
                            }
                            break;
                        case PieceColor.Black:
                            if (board.Tiles[i].AvailableMoves.Count > 0)
                            {
                                BlackMate = false;
                                Stalemate = false;
                                i = board.Tiles.Length;
                            }
                            break;
                    }
                }
            }
        }

        public static bool CanUpgradePawns(Board board)
        {
            // upgrade any pawns on the opposite end
            switch (board.Variant)
            {
                case GameVariant.Glinsky:
                case GameVariant.McCooey:
                    if (board.Tiles[5].Side == PieceColor.White && board.Tiles[5].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[12].Side == PieceColor.White && board.Tiles[12].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[20].Side == PieceColor.White && board.Tiles[20].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[29].Side == PieceColor.White && board.Tiles[29].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[39].Side == PieceColor.White && board.Tiles[39].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[50].Side == PieceColor.White && board.Tiles[50].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[60].Side == PieceColor.White && board.Tiles[60].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[69].Side == PieceColor.White && board.Tiles[69].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[77].Side == PieceColor.White && board.Tiles[77].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[84].Side == PieceColor.White && board.Tiles[84].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[90].Side == PieceColor.White && board.Tiles[90].Piece == PieceType.Pawn) return true;

                    if (board.Tiles[0].Side == PieceColor.Black && board.Tiles[0].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[6].Side == PieceColor.Black && board.Tiles[6].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[13].Side == PieceColor.Black && board.Tiles[13].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[21].Side == PieceColor.Black && board.Tiles[21].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[30].Side == PieceColor.Black && board.Tiles[30].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[40].Side == PieceColor.Black && board.Tiles[40].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[51].Side == PieceColor.Black && board.Tiles[51].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[61].Side == PieceColor.Black && board.Tiles[61].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[70].Side == PieceColor.Black && board.Tiles[70].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[78].Side == PieceColor.Black && board.Tiles[78].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[85].Side == PieceColor.Black && board.Tiles[85].Piece == PieceType.Pawn) return true;
                    break;
                case GameVariant.Shafran:
                    if (board.Tiles[5].Side == PieceColor.White && board.Tiles[5].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[12].Side == PieceColor.White && board.Tiles[12].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[20].Side == PieceColor.White && board.Tiles[20].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[29].Side == PieceColor.White && board.Tiles[29].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[39].Side == PieceColor.White && board.Tiles[39].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[48].Side == PieceColor.White && board.Tiles[48].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[56].Side == PieceColor.White && board.Tiles[56].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[63].Side == PieceColor.White && board.Tiles[63].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[69].Side == PieceColor.White && board.Tiles[69].Piece == PieceType.Pawn) return true;

                    if (board.Tiles[0].Side == PieceColor.Black && board.Tiles[0].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[6].Side == PieceColor.Black && board.Tiles[6].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[13].Side == PieceColor.Black && board.Tiles[13].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[21].Side == PieceColor.Black && board.Tiles[21].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[30].Side == PieceColor.Black && board.Tiles[30].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[40].Side == PieceColor.Black && board.Tiles[40].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[49].Side == PieceColor.Black && board.Tiles[49].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[57].Side == PieceColor.Black && board.Tiles[57].Piece == PieceType.Pawn) return true;
                    else if (board.Tiles[64].Side == PieceColor.Black && board.Tiles[64].Piece == PieceType.Pawn) return true;
                    break;
            }

            return false;
        }

        public static void UpgradePawns(ref Board board, PieceType type)
        {
            // upgrade any pawns on the opposite end
            switch (board.Variant)
            {
                case GameVariant.Glinsky:
                case GameVariant.McCooey:
                    if (board.Tiles[5].Side == PieceColor.White && board.Tiles[5].Piece == PieceType.Pawn) board.Tiles[5].Piece = type;
                    else if (board.Tiles[12].Side == PieceColor.White && board.Tiles[12].Piece == PieceType.Pawn) board.Tiles[12].Piece = type;
                    else if (board.Tiles[20].Side == PieceColor.White && board.Tiles[20].Piece == PieceType.Pawn) board.Tiles[20].Piece = type;
                    else if (board.Tiles[29].Side == PieceColor.White && board.Tiles[29].Piece == PieceType.Pawn) board.Tiles[29].Piece = type;
                    else if (board.Tiles[39].Side == PieceColor.White && board.Tiles[39].Piece == PieceType.Pawn) board.Tiles[39].Piece = type;
                    else if (board.Tiles[50].Side == PieceColor.White && board.Tiles[50].Piece == PieceType.Pawn) board.Tiles[50].Piece = type;
                    else if (board.Tiles[60].Side == PieceColor.White && board.Tiles[60].Piece == PieceType.Pawn) board.Tiles[60].Piece = type;
                    else if (board.Tiles[69].Side == PieceColor.White && board.Tiles[69].Piece == PieceType.Pawn) board.Tiles[69].Piece = type;
                    else if (board.Tiles[77].Side == PieceColor.White && board.Tiles[77].Piece == PieceType.Pawn) board.Tiles[77].Piece = type;
                    else if (board.Tiles[84].Side == PieceColor.White && board.Tiles[84].Piece == PieceType.Pawn) board.Tiles[84].Piece = type;
                    else if (board.Tiles[90].Side == PieceColor.White && board.Tiles[90].Piece == PieceType.Pawn) board.Tiles[90].Piece = type;

                    if (board.Tiles[0].Side == PieceColor.Black && board.Tiles[0].Piece == PieceType.Pawn) board.Tiles[0].Piece = type;
                    else if (board.Tiles[6].Side == PieceColor.Black && board.Tiles[6].Piece == PieceType.Pawn) board.Tiles[6].Piece = type;
                    else if (board.Tiles[13].Side == PieceColor.Black && board.Tiles[13].Piece == PieceType.Pawn) board.Tiles[13].Piece = type;
                    else if (board.Tiles[21].Side == PieceColor.Black && board.Tiles[21].Piece == PieceType.Pawn) board.Tiles[21].Piece = type;
                    else if (board.Tiles[30].Side == PieceColor.Black && board.Tiles[30].Piece == PieceType.Pawn) board.Tiles[30].Piece = type;
                    else if (board.Tiles[40].Side == PieceColor.Black && board.Tiles[40].Piece == PieceType.Pawn) board.Tiles[40].Piece = type;
                    else if (board.Tiles[51].Side == PieceColor.Black && board.Tiles[51].Piece == PieceType.Pawn) board.Tiles[51].Piece = type;
                    else if (board.Tiles[61].Side == PieceColor.Black && board.Tiles[61].Piece == PieceType.Pawn) board.Tiles[61].Piece = type;
                    else if (board.Tiles[70].Side == PieceColor.Black && board.Tiles[70].Piece == PieceType.Pawn) board.Tiles[70].Piece = type;
                    else if (board.Tiles[78].Side == PieceColor.Black && board.Tiles[78].Piece == PieceType.Pawn) board.Tiles[78].Piece = type;
                    else if (board.Tiles[85].Side == PieceColor.Black && board.Tiles[85].Piece == PieceType.Pawn) board.Tiles[85].Piece = type;
                    break;
                case GameVariant.Shafran:
                    if (board.Tiles[5].Side == PieceColor.White && board.Tiles[5].Piece == PieceType.Pawn) board.Tiles[5].Piece = type;
                    else if (board.Tiles[12].Side == PieceColor.White && board.Tiles[12].Piece == PieceType.Pawn) board.Tiles[12].Piece = type;
                    else if (board.Tiles[20].Side == PieceColor.White && board.Tiles[20].Piece == PieceType.Pawn) board.Tiles[20].Piece = type;
                    else if (board.Tiles[29].Side == PieceColor.White && board.Tiles[29].Piece == PieceType.Pawn) board.Tiles[29].Piece = type;
                    else if (board.Tiles[39].Side == PieceColor.White && board.Tiles[39].Piece == PieceType.Pawn) board.Tiles[39].Piece = type;
                    else if (board.Tiles[48].Side == PieceColor.White && board.Tiles[48].Piece == PieceType.Pawn) board.Tiles[48].Piece = type;
                    else if (board.Tiles[56].Side == PieceColor.White && board.Tiles[56].Piece == PieceType.Pawn) board.Tiles[56].Piece = type;
                    else if (board.Tiles[63].Side == PieceColor.White && board.Tiles[63].Piece == PieceType.Pawn) board.Tiles[63].Piece = type;
                    else if (board.Tiles[69].Side == PieceColor.White && board.Tiles[69].Piece == PieceType.Pawn) board.Tiles[69].Piece = type;

                    if (board.Tiles[0].Side == PieceColor.Black && board.Tiles[0].Piece == PieceType.Pawn) board.Tiles[0].Piece = type;
                    else if (board.Tiles[6].Side == PieceColor.Black && board.Tiles[6].Piece == PieceType.Pawn) board.Tiles[6].Piece = type;
                    else if (board.Tiles[13].Side == PieceColor.Black && board.Tiles[13].Piece == PieceType.Pawn) board.Tiles[13].Piece = type;
                    else if (board.Tiles[21].Side == PieceColor.Black && board.Tiles[21].Piece == PieceType.Pawn) board.Tiles[21].Piece = type;
                    else if (board.Tiles[30].Side == PieceColor.Black && board.Tiles[30].Piece == PieceType.Pawn) board.Tiles[30].Piece = type;
                    else if (board.Tiles[40].Side == PieceColor.Black && board.Tiles[40].Piece == PieceType.Pawn) board.Tiles[40].Piece = type;
                    else if (board.Tiles[49].Side == PieceColor.Black && board.Tiles[49].Piece == PieceType.Pawn) board.Tiles[49].Piece = type;
                    else if (board.Tiles[57].Side == PieceColor.Black && board.Tiles[57].Piece == PieceType.Pawn) board.Tiles[57].Piece = type;
                    else if (board.Tiles[64].Side == PieceColor.Black && board.Tiles[64].Piece == PieceType.Pawn) board.Tiles[64].Piece = type;
                    break;
            }
        }
    }
}